import os
import sys
import wx
import random

import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
    FigureCanvasWxAgg as FigCanvas, \
    NavigationToolbar2WxAgg as NavigationToolbar
import matplotlib.pyplot as plt
import numpy as np
import pylab

from mod import MainFrame as ModifyAFHMapDialog

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "AFH Alogrithm Demo Console", style = wx.DEFAULT_FRAME_STYLE)
        #self.p = wx.Panel(self)
        self.count = 0
        self.inputLabel = wx.StaticText(self, -1, "Please input the BER of each channel:")
        self.input = wx.TextCtrl(self, -1, size=(200, 100), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
        self.inputSubmit = wx.Button(self, -1, "Compute")
        self.genAFHMap = wx.Button(self, -1, "Generate AFH_Map")
        self.modAFHMap = wx.Button(self, -1, "Modify AFH_Map")
        #self.showGraph = wx.Button(self, -1, "Show Graph")
        self.outputLabel = wx.StaticText(self, -1, "Occupied Channel Prediction:")
        self.output = wx.TextCtrl(self, -1)
        
        #self.gauge = wx.Gauge(self, -1, 20)
		
        self.timer = wx.Timer(self)
        self.timer.Start(100)
        
        self.genAFH_dialog = GenAFHMapDialog(self)
        
        self.__make_menu()
        self.__do_layout()
        self.__attach_event()  
	
    def __make_menu(self):
		#-- File Menu 
        FileMenu = wx.Menu()
        Open = FileMenu.Append(-1, "&Open\tCtrl-O")
        FileMenu.AppendSeparator()
        exit = FileMenu.Append(-1, "E&xit\tCtrl-Q")
        
        #self.Bind(wx.EVT_MENU, self.OnExit, exit)
        
        #-- Edit Menu
        EditMenu = wx.Menu()
        Undo = EditMenu.Append(-1, "&Undo\tCtrl-Z")
        EditMenu.AppendSeparator()
        Cut = EditMenu.Append(-1, "Cu&t\tCtrl-X")
        Copy = EditMenu.Append(-1, "&Copy\tCtrl-C")
        Paste = EditMenu.Append(-1, "&Paste\tCtrl-V")
        
        #-- Help Menu
        HelpMenu = wx.Menu()
        About = HelpMenu.Append(-1, "&About")
        HelpMenu.AppendSeparator()
        GPCHelp = HelpMenu.Append(-1, "&GPC Help\tF1")
        
        ## Append menu in MenuBar
        menuBar = wx.MenuBar()
        menuBar.Append(FileMenu, "&File")
        menuBar.Append(EditMenu, "&Edit")
        menuBar.Append(HelpMenu, "&Help")
        
        self.SetMenuBar(menuBar)        
        
    def __do_layout(self):
        self.SetMaxSize((700, 800))		
        self.input.SetMinSize((210, 300))
        self.output.SetMinSize((200, 300))
        
        sizer_v_input = wx.BoxSizer(wx.VERTICAL)
        sizer_v_input.Add(self.inputLabel, 0, wx.ALL, 5)
        sizer_v_input.Add(self.input, 0, wx.ALL, 5)
        sizer_h_submit = wx.BoxSizer(wx.HORIZONTAL)
        sizer_h_submit.Add(self.inputSubmit, 0, wx.ALL, 5)
        sizer_h_submit.Add(self.genAFHMap, 0, wx.ALL, 5)
        
        sizer_v_input.Add(sizer_h_submit, 0, wx.ALL, 0)
        #sizer_v_input.Add(self.showGraph, 0, wx.ALL, 5)
        sizer_v_output = wx.BoxSizer(wx.VERTICAL)
        sizer_v_output.Add(self.outputLabel, 0, wx.ALL, 5)
        sizer_v_output.Add(self.output, 0, wx.ALL, 5)
        sizer_v_output.Add(self.modAFHMap, 0, wx.ALL, 5)
        #sizer_v_output.Add(self.gauge, 0, wx.ALL, 5)
        
        sizer_h = wx.BoxSizer(wx.HORIZONTAL)
        sizer_h.Add(sizer_v_input, 0, wx.ALL, 5)
        sizer_h.Add(sizer_v_output, 0, wx.ALL, 5)
        
        sizer_v = wx.BoxSizer(wx.VERTICAL)
        sizer_v.Add(sizer_h, 0, wx.ALL | wx.EXPAND, 0)
		
        self.SetAutoLayout(1)
        self.SetSizer(sizer_v)
        sizer_v.Fit(self)
        sizer_v.SetSizeHints(self)
        self.Layout()
		
    def __attach_event(self):
		# The time handler of gauge
        #self.Bind(wx.EVT_TIMER, self.TimerHandler) 
        self.inputSubmit.Bind(wx.EVT_BUTTON, self.OnInputSubmit)
        self.genAFHMap.Bind(wx.EVT_BUTTON, self.OnGen)
        self.modAFHMap.Bind(wx.EVT_BUTTON, self.OnMod)
        #self.showGraph.Bind(wx.EVT_BUTTON, self.OnShowGraph)
   
    def OnInputSubmit(self, event):
        import_data = self.input.GetValue()
        import_data = import_data.split('\n')
        self.ber_data = ber_data = []
        for _d in import_data:
            _d = float(_d)
            ber_data.append(_d)
        
        bc = BadChannel()
        bc.ber_data = self.ber_data
        bc.Run()
        self.output.SetValue('The Channel ' + str(bc.bad_channel) + ' is occupied.')
        
        self.graph_dialog = GraphFrame()
        self.graph_dialog.ber_data = self.ber_data
        self.graph_dialog.bad_channel = bc.bad_channel
        
        self.graph_dialog.sense_channel = bc.sense_channel
        self.graph_dialog.draw_plot()
        #self.graph_dialog.Show()

    def OnGen(self, event):
        AFH_CHANNEL_USE = ''
        AFH_CHANNEL_MAP = ''
        BUSY_CHANNEL = self.graph_dialog.bad_channel
        x = range(78)
        y = range(78)
        for i in x:
            y[i] = 1
            
        for b in BUSY_CHANNEL:
            for i in x[5*b-6 : 5*b+15]:
                y[i] = 0
        
        for i in y:
            if i == 0:
                AFH_CHANNEL_USE += '0' 
            else:
                AFH_CHANNEL_USE += '1'
        #print y
        for i in x:
            if i == 0:
                AFH_CHANNEL_MAP += self.Gen0X([0,y[0],y[1],y[2]])
            elif i == 1:
                pass
            elif i % 4 == 3:
                AFH_CHANNEL_MAP += self.Gen0X(y[i:i+4])
            else:
                pass
        
        self.genAFH_dialog.AFH_output.SetValue(AFH_CHANNEL_USE)
        self.genAFH_dialog.AFH_map_output.SetValue(AFH_CHANNEL_MAP)

        self.genAFH_dialog.ShowModal()
        
    def OnMod(self, event):
        self.modAFH_dialog = ModifyAFHMapDialog()
        self.modAFH_dialog.Show()
        
    def Gen0X(self, n):
        x = ''
        l = ''
        for _n in n:
            l += str(_n)
        if l == '0000':
            x = '0'
        elif l == '0001':
            x = '1'
        elif l == '0010':
            x = '2'
        elif l == '0011':
            x = '3'
        elif l == '0100':
            x = '4'
        elif l == '0101':
            x = '5'
        elif l == '0110':
            x = '6'
        elif l == '0111':
            x = '7'
        elif l == '1000':
            x = '8'
        elif l == '1001':
            x = '9'
        elif l == '1010':
            x = 'A'
        elif l == '1011':
            x = 'B'
        elif l == '1100':
            x = 'C'
        elif l == '1101':
            x = 'D'
        elif l == '1110':
            x = 'E'
        elif l == '1111':
            x = 'F'
        return x
    
class GenAFHMapDialog(wx.Dialog):
    def __init__(self, *arg, **kwds):
        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
        self.show = True
        if kwds.has_key('show'):
            self.show = kwds['show']
            del kwds['show']
        wx.Dialog.__init__(self, *arg, **kwds)
        
        self.AFH_label = wx.StaticText(self, -1, 'The AFH Channel to Be Used is:')
        self.AFH_output = wx.TextCtrl(self, -1, size=(200, 100), style=wx.TE_MULTILINE)
        self.AFH_map_label = wx.StaticText(self, -1, 'The AFH Channel Map Value is:')
        self.AFH_map_output = wx.TextCtrl(self, -1, size=(150, -1))
    
        self.__do_layout()
        
    def __do_layout(self):
        self.SetMaxSize((300, 220))
        
        sizer_v_output = wx.BoxSizer(wx.VERTICAL)
        sizer_v_output.Add(self.AFH_label, 0, wx.ALL, 5)
        sizer_v_output.Add(self.AFH_output, 0, wx.ALL, 5)
        sizer_v_output.Add(self.AFH_map_label, 0, wx.ALL, 5)
        sizer_v_output.Add(self.AFH_map_output, 0, wx.ALL, 5)
        
        sizer_v = wx.BoxSizer(wx.VERTICAL)
        sizer_v.Add(sizer_v_output, 0, wx.ALL | wx.EXPAND, 0)
		
        self.SetAutoLayout(1)
        self.SetSizer(sizer_v)
        sizer_v.Fit(self)
        sizer_v.SetSizeHints(self)
        self.Layout()
    
class GraphFrame(wx.Frame):
    def __init__(self, *arg, **kwds):
        wx.Frame.__init__(self, None)
        self.ber_data = []
        self.bad_channel = []
        self.sense_channel = []
    
    def draw_plot(self):
        x = range(79)
        y = self.ber_data
        plt.figure(1)
        plt.subplots_adjust(hspace=0.4)
        plt.subplot(311)
        plt.plot(x, y)
        plt.bar(x, y, facecolor='g', edgecolor='w')
        #plt.xlabel('The Channel Number')
        plt.ylabel('The PER Value')
        plt.title('PER Value of Each Channel')
        plt.axis([0, 78, 0, 20.0])
        plt.grid(True)
        
        plt.subplot(312)
        for i in x:
            y[i] = 1
        plt.bar(x, y, facecolor='g', edgecolor='w')
        for i in self.sense_channel:
            y[i] = 0
        plt.bar(x, y, facecolor='w', edgecolor='w')
        plt.title('The Channel Scaned')
        plt.axis([0, 78, 0, 1])
        plt.grid(False)
        
        plt.subplot(313)
        for i in x:
            y[i] = 1
        plt.bar(x, y, facecolor='r', edgecolor='w')
        for b in self.bad_channel:
            for i in x[5*b-5 : 5*b+16]:
                y[i] = 0
        plt.bar(x, y, facecolor='g', edgecolor='w')
        plt.xlabel('The Channel Number')
        #plt.ylabel('The BER Value')
        plt.title('Occupied Channel Prediction')
        plt.axis([0, 78, 0, 1])
        plt.grid(False)
        
        plt.show()
    
""" The AFH alogrithm part!
Slope(x)
Average(x)
BadChannel(data)
"""

class BadChannel(object):
    def __init__(self):
        self.ber_data = []
        self.sense_channel = []
        self.bad_channel = []
        
    def Slope(self, y):
        x = [1,2,3,4,5]
        sumx = sumy = sumxx = sumxy = 0
        for n in range(5):
            sumx = sumx + x[n]
            sumy = sumy + y[n]
            sumxy = sumxy + x[n] * y[n]
            sumxx = sumxx + x[n] * x[n]
        
        averx = sumx/5
        avery = sumy/5
        if sumxx - 5*averx*averx == 0:
            return 0
        b = (sumxy - 5*averx*avery)/(sumxx - 5*averx*averx)
        return b
        
    def Average(self, x):
        sum = 0
        for _x in x:
            sum = sum + _x
        aver = sum/len(x)
        return aver
        
    def Run(self):
        # Initialization
        W = 22
        TW = 0.75
        check = [0,0,0,0,0,0,0,0,0,0,0]
        average = [0,0,0,0,0,0,0,0,0,0,0]
        ifchecked = [0,0,0,0,0,0,0,0,0,0,0]
        prediction = [0,0,0,0,0,0,0,0,0,0,0]
        
        # PER[79]
        PER = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0]

        # grouplist[11][22]
        grouplist = []
        l = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
        for c in check:
            grouplist.append(l)
            
        # data from AFH_data.txt
        data = self.ber_data
        
        for _t in range(22):
            m = random.randint(0,78)
            self.sense_channel.append(m)
            print 'channel number is: ' + str(m)
            print 'PER/BER is: ' + str(data[m-1])
            
            PER[m-1] = data[m-1]

            s = int(m/5) + 1
            t = (m + 2)%5
            if m > 54:
                s = 11
            i = 0
            while (t + i*5 < 22):
                if (s >= 1):
                    grouplist[s-1][t + i*5 - 1] = PER[m-1]
                    sum = 0
                    for n in grouplist[s-1]:
                        sum = sum + n
                    average[s-1] = sum/W
                    #print average[s-1]
                    if(average[s-1] > 0.75):
                        check[s-1] = 1
                s = s - 1
                i = i + 1
        print check

        Sl = 0.45
        for i in range(10):
            if check[i] == 1 and ifchecked[i] == 0:
                if check[i+1] == 1:
                    j = (i + 1) * 5-1+3
                     
                    temp = range(5)
                    for n in range(5):
                        PER[j-1] = data[j-1]
                        temp[n] = PER[j-1]
                        if temp[n]>7:
                            temp[n]=7
                        #if self.sense_channel.count(j) == 0:
                        #    self.sense_channel.append(j)
                        j = j + 1 
                        
                    
                    if self.Slope(temp) < 0.25 and self.Slope(temp) > -0.25 and self.Average(temp) > 5.0:
                        prediction[i] = 1
                        ifchecked[i] = 1
                        ifchecked[i+1] = 1
                        print 'Wi-Fi channel ' + str(i+1) + ' is occupied.'
                        print 'Bluetooth channels ' + str(5 * i - 1) + ' to ' + \
                        str(5 * i + 20) + ' should be bad ones.'
                        # -----
                        self.bad_channel.append(i+1)
                        for k in range(5):
                            if self.sense_channel.count(j-k-1) == 0:
                                self.sense_channel.append(j-k-1)

                        
                    if self.Slope(temp) > Sl and self.Average(temp) > 2.0:
                        prediction[i+1] = 1
                        ifchecked[i] = 1
                        ifchecked[i+1] = 1
                        print 'Wi-Fi channel ' + str(i+2) + ' is occupied.'
                        print 'Bluetooth channels ' + str(5 *(i+1)-1) + ' to ' + \
                        str(5 *(i+1)+20) + ' should be bad ones.'
                        # -----
                        self.bad_channel.append(i+2)
                        for k in range(5):
                            if self.sense_channel.count(j-k-1) == 0:
                                self.sense_channel.append(j-k-1)
                        
                else:
                    temp = range(5)
                    j = 5 * i + 1+3
                    for n in range(5):
                        temp[n] = PER[j-1]
                        if temp[n]>7:
                            temp[n]=7
                        #if self.sense_channel.count(j) == 0:
                        #    self.sense_channel.append(j)
                        j = j + 1
                    
                    if self.Slope(temp) > Sl and self.Average(temp) > 2.0:
                        prediction[i] = 1
                        ifchecked[i] = 1
                        ifchecked[i+1] = 1
                        print 'Wi-Fi channel ' + str(i+1) + ' is occupied.'
                        print 'Bluetooth channels ' + str(5 * i-1) + ' to ' + \
                        str(5 * i + 20) + ' should be bad ones.'
                        # -----
                        self.bad_channel.append(i+1)
                        for k in range(5):
                            if self.sense_channel.count(j-k-1) == 0:
                                self.sense_channel.append(j-k-1)
                        
        print self.sense_channel
        print self.bad_channel
        return self.bad_channel
	
if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()
